import { computed, toRaw, unref } from 'vue';
import type { FormSchema, NamePath, Recordable } from '../types/form';
import { dateUtil, type UseFormActionContext } from '../types/formEvents';
import { cloneDeep, get, isArray, isNil, isObject, isString, set, uniqBy } from 'lodash-es';
import { deepMerge } from '../common/gloab';
import { isDef } from '../common/is';
import { dateItemType, handleInputNumberValue, isIncludeSimpleComponents } from './helper';
import type { OptionsItem } from '../types/formItem';

export const getOptions = computed(() => {
  return (e: any) => {
    const { labelField = 'label', valueField = 'value', options } = e;
    const optionArr = unref(options).reduce(
      (prev: { label: any; value: any }[], next: Recordable) => {
        if (next) {
          prev.push({
            label: get(next, labelField),
            value: get(next, valueField),
            ...next,
          });
        }
        return prev;
      },
      [] as OptionsItem[],
    );
    return optionArr;
  };
});

function tryConstructObject(field: string, values: Recordable = {}): Recordable | undefined {
  const pattern = /^\{(.+)\}$/;
  if (pattern.test(field)) {
    const match = field.match(pattern);
    if (match && match[1]) {
      const keys = match[1].split(',');
      if (!keys.length) {
        return;
      }

      const result = {};
      keys.forEach((k) => {
        set(result, k.trim(), values[k.trim()]);
      });

      return Object.values(result).filter(Boolean).length ? result : undefined;
    }
  }
}
function tryConstructArray(field: string, values: Recordable = {}): any[] | undefined {
  const pattern = /^\[(.+)\]$/;
  if (pattern.test(field)) {
    const match = field.match(pattern);
    if (match && match[1]) {
      const keys = match[1].split(',');
      if (!keys.length) {
        return undefined;
      }

      const resultd: any = [];
      keys.forEach((k, index) => {
        set(resultd, index, values[k.trim()]);
      });

      return resultd.filter(Boolean).length ? resultd : undefined;
    }
  }
}
export function useFormEvents({
  emit,
  getProps,
  formModel,
  getSchema,
  defaultValueRef,
  formElRef,
  schemaRef,
  handleFormValues,
}: UseFormActionContext) {
  /**
   * @description: Is it time
   */
  function itemIsDateType(key: string) {
    return unref(getSchema).some((item) => {
      return item.field === key ? dateItemType.includes(item.component) : false;
    });
  }
  // 验证具体的某个字段
  async function validateField(name?: string | undefined) {
    return unref(formElRef)?.validateField(name);
  }
  //清理某个字段的表单验证信息
  async function clearValidate(name?: string | undefined) {
    return unref(formElRef)?.clearValidate(name);
  }
  // 验证
  function validate(nameList?: NamePath[] | undefined) {
    return new Promise(async (resolve, reject) => {
      try {
        await unref(formElRef)?.validate(nameList);
        const data = handleFormValues(formModel);
        resolve(data);
      } catch (error) {
        reject(error);
      }
    });
  }
  // 重置表单
  async function resetFields(): Promise<void> {
    const formEl = unref(formElRef);
    const { submitOnReset } = unref(getProps);
    if (!formEl) return;
    await formEl.resetFields();
    setDefaultValue(unref(getSchema));
    emit && emit('reset', toRaw(formModel));
    submitOnReset && handleSubmit();
  }

  // 获取表单值
  function getFieldsValue(): Recordable {
    const formEl = unref(formElRef);
    if (!formEl) return {};
    return handleFormValues(formModel);
  }

  // 提交事件
  async function handleSubmit(): Promise<void> {
    try {
      const values = await validate();
      if (values) {
        emit && emit('submit', values);
      }
    } catch (error: any) {
      if (error?.outOfDate === false && error?.errorFields) {
        return;
      }
      console.error(JSON.stringify(error));
    }
  }

  /**
   * 设置表单值
   */
  async function setFieldsValue(values: Recordable): Promise<void> {
    const fields = unref(getSchema)
      .map((item) => item.field)
      .filter(Boolean);

    // key 支持 a.b.c 的嵌套写法
    const delimiter = '.';
    const nestKeyArray = fields.filter((item) => String(item).indexOf(delimiter) >= 0);

    const validKeys: string[] = [];

    fields.forEach((key) => {
      const schema = unref(getSchema).find((item) => item.field === key);
      let value = get(values, key);
      const hasKey = Reflect.has(values, key);
      // 字符串 数字处理
      value = handleInputNumberValue(schema?.component, value);
      const { componentProps } = schema || {};
      // eslint-disable-next-line no-underscore-dangle
      let _props = componentProps as any;
      if (typeof componentProps === 'function') {
        _props = _props({
          formModel: unref(formModel),
          schema,
          formActionType: {
            validate,
            validateField,
            resetFields,
            getFieldsValue,
            handleSubmit,
            setFieldsValue,
            updateSchema,
            appendSchemaByField,
            removeSchemaByField,
          },
          isInit: true, //是否setFieldsValue过来(初始赋值)
        });
      }

      const constructValue = tryConstructArray(key, values) || tryConstructObject(key, values);

      // 0| '' is allow
      if (hasKey || !!constructValue) {
        const fieldValue = constructValue || value;
        // 时间类型
        if (itemIsDateType(key)) {
          // console.log("fieldValue", fieldValue, key)

          if (Array.isArray(fieldValue)) {
            const arr: any[] = [];
            for (const ele of fieldValue) {
              arr.push(ele ? dateUtil(ele) : null);
            }
            unref(formModel)[key] = arr;
          } else {
            // eslint-disable-next-line no-nested-ternary
            unref(formModel)[key] = fieldValue
              ? _props?.valueFormat
                ? fieldValue
                : dateUtil(fieldValue)
              : null;
          }
        } else {
          unref(formModel)[key] = fieldValue;
        }
        if (_props?.onChange) {
          _props?.onChange(fieldValue);
        }
        validKeys.push(key);
        // 验证具体
        validateField(key).catch((err) => {
          console.log('ee', err);
        });
      } else {
        nestKeyArray.forEach((nestKey: string) => {
          try {
            const valued = nestKey.split('.').reduce((out, item) => out[item], values);
            if (isDef(valued)) {
              unref(formModel)[nestKey] = unref(valued);
              validKeys.push(nestKey);
            }
          } catch (e) {
            // key not exist
            if (isDef(defaultValueRef.value[nestKey])) {
              unref(formModel)[nestKey] = cloneDeep(unref(defaultValueRef.value[nestKey]));
            }
          }
        });
      }
    });

    // 设置第二个参数
    const fieldTwo: string[] = [];
    unref(getSchema).forEach((one: FormSchema) => {
      if (one.fieldTwo) {
        fieldTwo.push(one.fieldTwo);
      }
    });
    fieldTwo.length &&
      fieldTwo.forEach((key2: string) => {
        const fieldTwo = get(values, key2);
        unref(formModel)[key2] = fieldTwo;
      });
  }

  async function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
    let updateData: Partial<FormSchema>[] = [];
    if (isObject(data)) {
      updateData.push(data as FormSchema);
    }
    if (isArray(data)) {
      updateData = [...data];
    }

    const hasField = updateData.every(
      (item) =>
        isIncludeSimpleComponents(item.component) || (Reflect.has(item, 'field') && item.field),
    );

    if (!hasField) {
      console.warn('需要更新的表单架构数组的所有子级必须包含“field”字段');
      return;
    }
    const schema: FormSchema[] = [];
    const updatedSchema: FormSchema[] = [];
    unref(getSchema).forEach((val) => {
      const updatedItem = updateData.find((item) => val.field === item.field);
      if (updatedItem) {
        // 递归合并
        const newSchema = deepMerge(val, updatedItem);
        updatedSchema.push(newSchema as FormSchema);
        schema.push(newSchema as FormSchema);
      } else {
        schema.push(val);
      }
    });
    setDefaultValue(updatedSchema);
    schemaRef.value = uniqBy(schema, 'field');
  }

  function setDefaultValue(data: FormSchema | FormSchema[]) {
    let schemas: FormSchema[] = [];
    if (isObject(data)) {
      schemas.push(data as FormSchema);
    }
    if (isArray(data)) {
      schemas = [...data];
    }

    const obj: Recordable = {};
    const currentFieldsValue = getFieldsValue();
    schemas.forEach((item) => {
      if (
        !isIncludeSimpleComponents(item.component) &&
        Reflect.has(item, 'field') &&
        item.field &&
        !isNil(item.defaultValue) &&
        (!(item.field in currentFieldsValue) || isNil(currentFieldsValue[item.field]))
      ) {
        obj[item.field] = item.defaultValue;
      }
    });
    setFieldsValue(obj);
  }

  /**
   * @description: Delete based on field name
   */
  async function removeSchemaByField(fields: string | string[]): Promise<void> {
    const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
    if (!fields) {
      return;
    }

    let fieldList: string[] = isString(fields) ? [fields] : fields;
    if (isString(fields)) {
      fieldList = [fields];
    }
    for (const field of fieldList) {
      _removeSchemaByFeild(field, schemaList);
    }
    schemaRef.value = schemaList;
  }

  /**
   * @description: Delete based on field name
   */
  function _removeSchemaByFeild(field: string, schemaList: FormSchema[]): void {
    if (isString(field)) {
      const index = schemaList.findIndex((schema) => schema.field === field);
      if (index !== -1) {
        delete formModel[field];
        schemaList.splice(index, 1);
      }
    }
  }

  /**
   * @description: Insert after a certain field, if not insert the last
   */
  async function appendSchemaByField(
    schema: FormSchema | FormSchema[],
    prefixField?: string,
    first = false,
  ) {
    const schemaList: FormSchema[] = cloneDeep(unref(getSchema));

    const index = schemaList.findIndex((schema) => schema.field === prefixField);
    const _schemaList = isObject(schema) ? [schema as FormSchema] : (schema as FormSchema[]);
    if (!prefixField || index === -1 || first) {
      first ? schemaList.unshift(..._schemaList) : schemaList.push(..._schemaList);
      schemaRef.value = schemaList;
      setDefaultValue(schema);
      return;
    }
    if (index !== -1) {
      schemaList.splice(index + 1, 0, ..._schemaList);
    }
    setDefaultValue(schema);

    schemaRef.value = schemaList;
  }

  return {
    validate,
    validateField,
    resetFields,
    getFieldsValue,
    handleSubmit,
    setFieldsValue,
    updateSchema,
    appendSchemaByField,
    removeSchemaByField,
    clearValidate,
  };
}
